import path from "path";
import winston, { Logger } from "winston";
import fs from "fs-extra";
import DailyRotateFile from "winston-daily-rotate-file";
import { Request, Response, NextFunction } from "express";

declare global {
  namespace Express {
    /**
     * @description 扩展 Express Request 对象
     */
    interface Request {
      /**
       * @property {Logger} logger - 日志记录器实例
       */
      logger?: Logger;
    }

    interface User {
      id: string;
      username: string;
      role: string;
      // 添加其他用户属性...
    }
  }
}

// 确定路由模块名称
const getRouteModule = (url: string): string => {
  // 从URL中提取模块名称，如：/api/v1/user/profile => user
  const parts = url.split("/");
  // 查找 v1 之后的部分作为模块名
  const v1Index = parts.findIndex((part) => part === "v1");
  if (v1Index !== -1 && parts.length > v1Index + 1) {
    return parts[v1Index + 1];
  }
  return "general"; // 默认模块名称
};

// 创建日志记录器实例
const createLogger = (username: string, routeModule: string) => {
  // 确保日志目录存在
  const logDir = path.join(
    __dirname,
    "..",
    "..",
    "logs",
    username,
    routeModule
  );
  fs.ensureDirSync(logDir);

  return winston.createLogger({
    level: "info",
    format: winston.format.combine(
      winston.format.timestamp({
        format: "YYYY-MM-DD HH:mm:ss",
      }),
      winston.format.json()
    ),
    defaultMeta: { username, module: routeModule },
    transports: [
      // 控制台输出
      new winston.transports.Console({
        format: winston.format.combine(
          winston.format.colorize(),
          winston.format.printf(
            (info) =>
              `${info.timestamp} ${info.level}: [${info.username}][${info.module}] ${info.message}`
          )
        ),
      }),
      // 文件输出 - 按日期轮转
      new DailyRotateFile({
        dirname: logDir,
        filename: `%DATE%.log`,
        datePattern: "YYYY-MM-DD",
        maxSize: "20m",
        maxFiles: "14d",
      }),
    ],
  });
};

/**
 * @title 请求日志中间件
 * @description 该中间件会记录每个请求的详细信息，包括请求方法、URL、IP地址、请求体和查询参数等，并将日志按照用户和模块分类存储到对应的日志文件中。
 */
const requestLogger = (req: Request, res: Response, next: NextFunction) => {
  const start = Date.now();

  // 获取用户名（从JWT令牌中）
  let username = "anonymous";
  if (req.user && req.user.username) {
    username = req.user.username;
  }

  // 获取路由模块
  const routeModule = getRouteModule(req.originalUrl);

  // 创建日志记录器
  const logger = createLogger(username, routeModule);

  // 记录请求信息
  logger.info(`${req.method} ${req.originalUrl}`, {
    method: req.method,
    url: req.originalUrl,
    ip: req.ip,
    body: req.method !== "GET" ? JSON.stringify(req.body) : "",
    query: JSON.stringify(req.query),
  });

  // 在响应对象中添加日志记录器，方便路由处理函数使用
  req.logger = logger;

  // 响应完成后记录响应信息
  res.on("finish", () => {
    const duration = Date.now() - start;
    const { statusCode } = res;

    logger.info(
      `${req.method} ${req.originalUrl} ${statusCode} - ${duration}ms`,
      {
        statusCode,
        duration,
        responseTime: duration,
      }
    );
  });

  next();
};

/**
 * @tittle 错误日志中间件
 * @description 该中间件会把错误信息按照用户/模块分类记录到对应的日志文件中
 *
 */
const errorLogger = (
  err: any,
  req: Request,
  res: Response,
  next: NextFunction
) => {
  // 获取用户名（从JWT令牌中）
  let username = "anonymous";
  if (req.user && req.user.username) {
    username = req.user.username;
  }

  // 获取路由模块
  const routeModule = getRouteModule(req.originalUrl);

  // 创建日志记录器
  const logger = createLogger(username, routeModule);

  // 记录错误信息
  logger.error("发生错误", {
    error: {
      message: err.message,
      stack: err.stack,
      name: err.name,
    },
    method: req.method,
    url: req.originalUrl,
    ip: req.ip,
  });

  next(err);
};

export { requestLogger, errorLogger };
